import os from "os";
import path from "path";
import { fileURLToPath  } from "node:url";
import { Worker } from "node:worker_threads";
import {buildUrl, getData, postData} from "./circle-common.js";
import gateway from "./circle-gateway.js";

/// <reference path="./circle-types.js" />

let globalWorkers = [];
const supportedArchs = ['x64'];
const supportedPlatforms = ['linux', 'win32', 'darwin'];

/**
 *
 * @param {string} address - the address
 * @return { Promise<MyBlockDataResponse | null>}
 */
export async function fetchMyBlockData(address) {
  const url = buildUrl(gateway.wallet.path.fetchMyBlockPath, {
    address
  });
  return getData(url, +gateway.http.timeoutRead);
}

/**
 *
 * @param {Object} input - the user input
 * @param {string} input.address - the address
 * @param {string} [input.ipPort] - the ip port
 * @param {string} [input.channelId] - the miner channel id.
 * @param {string} input.blockHeaderHexString - the block header hex string
 * @return {Promise<BooleanResponse>} - the response
 */
export async function postMyBlock(input) {
  const url =  buildUrl(gateway.wallet.path.postMyBlockPath);
  return postData(url, input, +gateway.http.timeoutWrite);
}

export function canMineBlock() {
  const arch = os.arch();
  const platform = os.platform();
  console.log('mine block arch:', arch, 'platform:', platform);
  if (!supportedArchs.includes(arch)) {
    console.log(`not support arch: ${arch}, supported machines: ${JSON.stringify(supportedArchs)}`);
    return false;
  }
  if (!supportedPlatforms.includes(platform)) {
    console.log(`not supported platform: ${platform}, supported platforms: ${JSON.stringify(supportedPlatforms)}`);
    return false;
  }

  return true;
}

/**
 * mine block locally
 * @param {string} blockHeaderHexString - the block header hex string
 * @param {number} threadCount - the thread count
 * @return {Promise<string>} - the response
 */
export async function mineBlock(blockHeaderHexString, threadCount) {
  const arch = os.arch();
  console.log('mine block arch:', arch);
  if (!canMineBlock()) {
    throw new Error(`not support arch: ${arch} platform: ${os.platform()}`);
  }
  switch (os.platform()) {
    case 'darwin':
      return mineBlockInPlatform("mac", arch, blockHeaderHexString, threadCount);
    case 'win32':
      return mineBlockInPlatform("windows", arch, blockHeaderHexString, threadCount);
    case 'linux':
      return mineBlockInPlatform("linux", arch, blockHeaderHexString, threadCount);
    case 'openbsd':
      return mineBlockInPlatform("openbsd", arch, blockHeaderHexString, threadCount);
    case 'freebsd':
      return mineBlockInPlatform("freebsd", arch, blockHeaderHexString, threadCount);
    default:
      throw new Error(`unsupported platform ${os.platform()}`);
  }
}

export function terminateAndClearWorkers() {
  for (const worker of globalWorkers) {
    if (worker && worker.isPending()) {
      worker.terminate();
      console.log("worker:", worker.index, "of", worker.threadCount, "terminated");
    }
  }
  globalWorkers = [];
}

/**
 * mine block in specific platform
 * @param {string} platform - the os platform
 * @param {string} arch - the architecture: arm64, x64
 * @param {string} blockHeaderHexString - the block header hex string
 * @param {number} threadCount - the thread count
 * @return {Promise<string>} - the response
 */
async function mineBlockInPlatform(platform, arch, blockHeaderHexString, threadCount) {
  terminateAndClearWorkers();
  for (let i = 0; i < threadCount; i++) {
    globalWorkers.push(createWorker({
      platform,
      arch,
      threadCount,
      blockHeaderHexString,
      index: i
    }));
  }

  try {
    const workerPromises = globalWorkers.map(worker => worker.promise);
    const result = await Promise.any(workerPromises);
    console.log('mine block success, platform:', platform, "threadCount:", threadCount, "result:", result);
    return result;
  } catch (err) {
    console.error("cannot mine block, platform:", platform, "threadCount:", threadCount, "error:", err);
    throw err;
  } finally {
    terminateAndClearWorkers();
  }
}

function createWorker({ platform, arch, threadCount, blockHeaderHexString, index }) {
  // Set initial state
  let isPending = true;
  let isRejected = false;
  let isFulfilled = false;
  let worker = {};

  //WORKER_CREATION

  const result = {};
  result.promise = new Promise(function (resolve, reject) {
    worker.on("message", (data) => {
      isFulfilled = true;
      isPending = false;
      resolve(data);
    });
    worker.on("error", (msg) => {
      isRejected = true;
      isPending = false;
      reject(new Error(`An error occurred: ${msg}`));
    });
  });
  result.index = index;
  result.threadCount = threadCount;
  result.isFulfilled = function() { return isFulfilled; };
  result.isPending = function() { return isPending; };
  result.isRejected = function() { return isRejected; };
  result.terminate = () => {
    worker.postMessage('terminate');
  };
  return result;
}
